# -*- coding: utf-8 -*-
"""
列出49选6的所有组合，并计算相对一批“三个数一注”的投注在该开奖号码下的：
- 三中三组数（恰好3个都在开奖号码中）
- 三中二组数（恰好2个在开奖号码中）
- 赔付金额（120*三中三 + 19*三中二）
- 投注金额（= 注数 × 每注下注额）
- 净收益金额（赔付金额 - 投注金额）

支持三种模式：
1) --topk K       仅保留收益最高的前K条（内存小、速度快，默认）
2) --bottomk K    仅保留收益最低的前K条（赔付金额升序）
3) --full         全量外排（用SQLite排序），体量巨大，请谨慎使用

用法示例：
    python all_combos_payout_export.py --inputs 香港1.txt 香港2.txt 香港3.txt 香港4.txt --topk 100000
    python all_combos_payout_export.py --inputs 香港1.txt 香港2.txt 香港3.txt 香港4.txt --bottomk 100000
    python all_combos_payout_export.py --inputs 香港1.txt 香港2.txt 香港3.txt 香港4.txt --full --processes 8
"""

import argparse
import csv
import os
import sqlite3
import sys
import tempfile
from collections import Counter
from itertools import combinations
from typing import Iterable, List, Tuple

# ------------------------------- 解析投注 ----------------------------------
def parse_three_numbers(line: str):
    import re
    nums = re.findall(r"\d+", line)
    if len(nums) < 4:
        return None
    a, b, c = map(int, nums[-3:])
    return tuple(sorted((a, b, c)))

def load_bets(paths: List[str]) -> Counter:
    triples = []
    for fp in paths:
        with open(fp, "r", encoding="utf-8", errors="ignore") as f:
            for i, line in enumerate(f):
                if i == 0 or not line.strip():
                    continue
                t = parse_three_numbers(line)
                if t:
                    triples.append(t)
    return Counter(triples)

# ------------------------------- 计数结构 ----------------------------------
def build_pair_triple_tables(unique_triples: Counter):
    """
    建 pair_count[i][j] 与 triple_count[i][j][k]（1..49索引，i<j<k）
    便于后续 O(C(6,2)+C(6,3)) 查表计算三中三/三中二。
    """
    pair_count = Counter()
    triple_count = Counter()
    for (a, b, c), cnt in unique_triples.items():
        triple_count[(a, b, c)] += cnt
        pair_count[(a, b)] += cnt
        pair_count[(a, c)] += cnt
        pair_count[(b, c)] += cnt
    return pair_count, triple_count

# ------------------------------- 计算函数 ----------------------------------
def evaluate_six_numbers(nums6: Tuple[int, int, int, int, int, int],
                         pair_count: Counter,
                         triple_count: Counter):
    """
    返回 (k33, k32, payout)
    k33 = sum_{三元组t⊆S} triple_count[t]
    k32 = sum_{二元组p⊆S} pair_count[p] - 3*k33
    payout = 120*k33 + 19*k32
    """
    a, b, c, d, e, f = nums6
    pairs = [(a,b),(a,c),(a,d),(a,e),(a,f),
             (b,c),(b,d),(b,e),(b,f),
             (c,d),(c,e),(c,f),
             (d,e),(d,f),
             (e,f)]
    k2plus = sum(pair_count.get(p, 0) for p in pairs)

    triples = [(a,b,c),(a,b,d),(a,b,e),(a,b,f),
               (a,c,d),(a,c,e),(a,c,f),
               (a,d,e),(a,d,f),
               (a,e,f),
               (b,c,d),(b,c,e),(b,c,f),
               (b,d,e),(b,d,f),
               (b,e,f),
               (c,d,e),(c,d,f),
               (c,e,f),
               (d,e,f)]
    k33 = sum(triple_count.get(t, 0) for t in triples)

    k32 = k2plus - 3 * k33
    payout = 120 * k33 + 19 * k32
    return k33, k32, payout

# ----------------------------- 组合枚举器 ----------------------------------
def iter_all_6_from_49() -> Iterable[Tuple[int, int, int, int, int, int]]:
    return combinations(range(1, 50), 6)

# ----------------------------- Top-K 模式 ----------------------------------
def run_topk(unique_triples: Counter, stake_per_bet: float, topk: int, out_csv: str):
    pair_count, triple_count = build_pair_triple_tables(unique_triples)
    bet_count = sum(unique_triples.values())

    import heapq
    heap = []  # (payout, k33, k32, nums6)
    for nums6 in iter_all_6_from_49():
        k33, k32, payout = evaluate_six_numbers(nums6, pair_count, triple_count)
        if len(heap) < topk:
            heapq.heappush(heap, (payout, k33, k32, nums6))
        else:
            if (payout, k33, k32) > heap[0][:3]:
                heapq.heapreplace(heap, (payout, k33, k32, nums6))

    rows = sorted(heap, key=lambda x: (x[0], x[1], x[2]), reverse=True)
    with open(out_csv, "w", newline="", encoding="utf-8-sig") as f:
        w = csv.writer(f)
        w.writerow(["序号","开奖号码","三中三组数","三中二组数","投注金额","赔付金额","净收益金额"])
        for idx, (payout, k33, k32, nums6) in enumerate(rows, 1):
            bet_amt = bet_count * stake_per_bet
            net = payout - bet_amt
            w.writerow([idx, " ".join(map(str, nums6)), k33, k32, bet_amt, payout, net])

    print(f"[TopK] 已导出：{out_csv}  （共 {len(rows)} 行）")

# --------------------------- Bottom-K 模式 ---------------------------------
def run_bottomk(unique_triples: Counter, stake_per_bet: float, bottomk: int, out_csv: str):
    pair_count, triple_count = build_pair_triple_tables(unique_triples)
    bet_count = sum(unique_triples.values())

    import heapq
    # 维护“最差K条”的最大堆（通过存负值实现），保证堆顶是当前最差K中的“最优”，便于替换
    heap = []  # (-payout, -k33, -k32, nums6)
    for nums6 in iter_all_6_from_49():
        k33, k32, payout = evaluate_six_numbers(nums6, pair_count, triple_count)
        item = (-payout, -k33, -k32, nums6)
        if len(heap) < bottomk:
            heapq.heappush(heap, item)
        else:
            if item > heap[0]:
                heapq.heapreplace(heap, item)

    # 还原并按“赔付金额升序、k33升序、k32升序”导出
    rows = [(-a, -b, -c, nums6) for (a,b,c,nums6) in heap]
    rows.sort(key=lambda x: (x[0], x[1], x[2]))
    with open(out_csv, "w", newline="", encoding="utf-8-sig") as f:
        w = csv.writer(f)
        w.writerow(["序号","开奖号码","三中三组数","三中二组数","投注金额","赔付金额","净收益金额"])
        for idx, (payout, k33, k32, nums6) in enumerate(rows, 1):
            bet_amt = bet_count * stake_per_bet
            net = payout - bet_amt
            w.writerow([idx, " ".join(map(str, nums6)), k33, k32, bet_amt, payout, net])

    print(f"[BottomK] 已导出：{out_csv}  （共 {len(rows)} 行）")

# ----------------------------- Full 模式 -----------------------------------
def run_full(unique_triples: Counter, stake_per_bet: float, out_csv: str, processes: int):
    """
    全量外排到SQLite，再ORDER BY导出CSV。
    体量巨大：13,983,816 行；磁盘占用&时间都很可观。
    """
    pair_count, triple_count = build_pair_triple_tables(unique_triples)
    bet_count = sum(unique_triples.values())

    tmpdir = tempfile.mkdtemp(prefix="lotto_full_")
    db_path = os.path.join(tmpdir, "results.db")
    print("[Full] SQLite 暂存：", db_path)

    conn = sqlite3.connect(db_path)
    cur = conn.cursor()
    cur.execute("""
        CREATE TABLE results(
            a INTEGER, b INTEGER, c INTEGER, d INTEGER, e INTEGER, f INTEGER,
            k33 INTEGER, k32 INTEGER, payout INTEGER
        )
    """)
    cur.execute("PRAGMA synchronous=OFF")
    cur.execute("PRAGMA journal_mode=WAL")
    conn.commit()

    batch = []
    BATCH_SIZE = 20000
    inserted = 0
    for nums6 in iter_all_6_from_49():
        k33, k32, payout = evaluate_six_numbers(nums6, pair_count, triple_count)
        batch.append((*nums6, k33, k32, payout))
        if len(batch) >= BATCH_SIZE:
            cur.executemany("INSERT INTO results VALUES (?,?,?,?,?,?,?,?,?)", batch)
            conn.commit()
            inserted += len(batch)
            print(f"  已插入: {inserted}")
            batch.clear()
    if batch:
        cur.executemany("INSERT INTO results VALUES (?,?,?,?,?,?,?,?,?)", batch)
        conn.commit()
        inserted += len(batch)
        batch.clear()
        print(f"  已插入: {inserted}")

    print("[Full] 建索引并导出CSV（按 赔付金额 DESC, k33 DESC, k32 DESC）")
    cur.execute("CREATE INDEX idx_pay ON results(payout DESC, k33 DESC, k32 DESC)")
    conn.commit()

    with open(out_csv, "w", newline="", encoding="utf-8-sig") as f:
        w = csv.writer(f)
        w.writerow(["序号","开奖号码","三中三组数","三中二组数","投注金额","赔付金额","净收益金额"])
        idx = 0
        for row in cur.execute("SELECT a,b,c,d,e,f,k33,k32,payout FROM results ORDER BY payout DESC, k33 DESC, k32 DESC"):
            idx += 1
            a,b,c,d,e,f,k33,k32,payout = row
            bet_amt = bet_count * stake_per_bet
            net = payout - bet_amt
            w.writerow([idx, f"{a} {b} {c} {d} {e} {f}", k33, k32, bet_amt, payout, net])

    conn.close()
    print(f"[Full] 已导出：{out_csv}  （共 13,983,816 行）")
    print(f"[Full] 临时目录未自动删除：{tmpdir}  可自行清理")

# ------------------------------- 主入口 ------------------------------------
def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--inputs", nargs="+", default=["香港1.txt","香港2.txt","香港3.txt","香港4.txt"],
                    help="投注数据文件（每行：序号 + 三个号码，首行是表头）")
    ap.add_argument("--stake", type=float, default=1.0, help="每注下注金额（用于计算投注总额与净收益）")
    mode = ap.add_mutually_exclusive_group()
    mode.add_argument("--topk", type=int, default=None, help="仅导出收益最高的前K条")
    mode.add_argument("--bottomk", type=int, default=None, help="仅导出收益最低的前K条")
    mode.add_argument("--full", action="store_true", help="全量外排（体量巨大）")
    ap.add_argument("--output", default="收益排序结果.csv", help="输出CSV文件名")
    ap.add_argument("--processes", type=int, default=1, help="full模式可选：进程数（当前实现为单进程插入）")
    args = ap.parse_args()

    unique_triples = load_bets(args.inputs)
    bet_count = sum(unique_triples.values())
    print(f"共读取投注注数：{bet_count}")

    if args.full:
        run_full(unique_triples, args.stake, args.output, args.processes)
    elif args.topk:
        run_topk(unique_triples, args.stake, args.topk, args.output)
    elif args.bottomk:
        run_bottomk(unique_triples, args.stake, args.bottomk, args.output)
    else:
        # 无参数时默认导出收益最高的前 100000 条
        run_topk(unique_triples, args.stake, 100000, args.output)

if __name__ == "__main__":
    main()
